package com.braveo.dict;

import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLDecoder;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Configuration;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.speech.tts.TextToSpeech;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Button;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.FrameLayout.LayoutParams;
import android.widget.TextView;


public class ExplanationView extends Activity {
	private String word;
	private String explain;
	private boolean speakingAll = false;
	
	private DictInitializer initializer;
	
	private final View.OnClickListener speakListener = new View.OnClickListener(){
		public void onClick(View view){
			TextToSpeech tts = TtsManager.getTts(getApplicationContext());
			if(tts != null)
				tts.speak(word, 0, null);
		}
	};
	
	private final View.OnClickListener bookListener = new View.OnClickListener(){
		public void onClick(View view){
			//updateDict();
			initializer.getDictMessageHandler().sendEmptyMessage(DictConst.MESSAGE_CHOOSE_DATABASE);
		}
	};
	
	private final View.OnClickListener finishListener = new View.OnClickListener(){
		public void onClick(View view){
			finish();
		}
	};
	
	private final View.OnClickListener addHistListener = new View.OnClickListener(){
		public void onClick(View view){
			BraveRememberDbAdapter.getInstance(ExplanationView.this).addHistory(word);
		}
	};
	
	private final View.OnClickListener nextListener = new View.OnClickListener(){
		public void onClick(View view){
			String tmpWord = BraveDictDbAdapter.getInstance(ExplanationView.this).getNextWord(word);
			if(tmpWord!=null){
				word = tmpWord;
				fillData();
			}
				
		}
	};
	

	final AtomicBoolean caught = new AtomicBoolean(false);
	
	private final View.OnClickListener catchListener = new View.OnClickListener(){
		
		public void onClick(View view){
			if(caught.compareAndSet(false, true)) {
				findViewById(R.id.explan_explan).setOnTouchListener(onExplanationViewTouchListener);
			}
			else
			{
				caught.set(false);
				findViewById(R.id.explan_explan).setOnTouchListener(null);
			}
				
		}
	};
	
	private final View.OnClickListener rangeListener = new View.OnClickListener() {
		public void onClick(View view){
			if(view == findViewById(R.id.BUp))
				fillRangeData(true);
			else
				fillRangeData(false);
		}		
	};
	
	private final View.OnClickListener caseListener = new View.OnClickListener() {
		public void onClick(View view){
			fillCaseData();
		}		
	};
	
	private final View.OnClickListener prevListener = new View.OnClickListener(){
		public void onClick(View view){
			String tmpWord = BraveDictDbAdapter.getInstance(ExplanationView.this).getPrevWord(word);
			if(tmpWord!=null){
				word = tmpWord;
				fillData();
			}
				
		}
	};
	
	private final View.OnClickListener speakAllListener = new View.OnClickListener(){
		public void onClick(View view){
			fillSpeakAllData();
		}
	};
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		
		//ttsManager = TtsManager.getInstance(this.getApplicationContext());
		
		this.setContentView(R.layout.explanation);
		
		Button b = (Button)findViewById(R.id.BSpeak);
				
		b.setOnClickListener(speakListener);
		
		b = (Button)findViewById(R.id.BBook);
		b.setOnClickListener(bookListener);
		
		b = (Button)findViewById(R.id.BFinish);
		b.setOnClickListener(finishListener);
		
		b = (Button)findViewById(R.id.BUp);
		b.setOnClickListener(rangeListener);

		b = (Button)findViewById(R.id.BDown);
		b.setOnClickListener(rangeListener);

		b = (Button)findViewById(R.id.BCase);
		b.setOnClickListener(caseListener);

		b = (Button)findViewById(R.id.BSpeakAll);
		b.setOnClickListener(speakAllListener);
		
		((Button)findViewById(R.id.BAddHist)).setOnClickListener(addHistListener);

		((Button)findViewById(R.id.BNext)).setOnClickListener(nextListener);

		((Button)findViewById(R.id.BPrev)).setOnClickListener(prevListener);

		((Button)findViewById(R.id.BCatch)).setOnClickListener(catchListener);
		
		
		initializer = DictInitializer.getInitializer(this);
		try{
			initializer.init();
		}catch(DictInitializer.InitialException e){
			if(e.getError() == DictInitializer.InitError.DB_NOT_FOUND){
				return;
			}
			else {
				throw new RuntimeException("Unknown Error: " + e.getError());
			}
		}
		
		parseIntent();
				
	}
	
	/**
	 * Parse the caller/intent for different view.
	 * <p>
	 * Android use intents for inter-activity communication. Intents are conceptually similar
	 * to an encapsulation of protocol in terms of inter-activity calls.
	 * </p>
	 * <p>
	 * Brave-dict was designed to provide dictionary services for other applications, like
	 * brave-book. This method was created as a refactoring result when I developed brave-book
	 * on Nov, 15, 2009.
	 * </p>
	 */
	private void parseIntent(){
		Intent ix = getIntent();
		if(ix == null ){
			return;
		}
		
		if(ix.getAction() != null){
			String act = ix.getAction();
			if(act.equals(Intent.ACTION_VIEW)){
				Uri uri = ix.getData();
				if(uri == null)
					return;
				if(!uri.getScheme().equals("dict"))
					return;
				String query = uri.getQuery();
				setWord(query);
				fillData();
			}
		}
		else {
			Bundle extras = this.getIntent().getExtras();
			if(extras == null)
				return;
			
			word = extras.getString("WORD");			
			fillData();
		}
		
	}
	
	
	
	public void setWord(String word){
		this.word = word;
	}
	
	/*public Handler getWordUpdateHandler(){
		return wordUpdateHandler;
	}*/
	
	private final static Pattern SPEAK_ALL_PATTERN = Pattern
		.compile("([^a-zA-Z]*)([a-zA-Z]+)([^a-zA-Z]*)");

	private ImageDataLoader fakeLoader = new ImageDataLoader() {
		
		public BraveImageObject loadImage(int imageId) {						
			InputStream is = ExplanationView.this.getResources()
					.openRawResource(R.drawable.dict_speakall_neg);
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
			
			byte[] buf = new byte[1024];
			
			try {
				int rlen;
				while((rlen = is.read(buf))!=-1) {
					baos.write(buf, 0, rlen);
				}
				is.close();
				return new BraveImageObject.Builder().setImageId(imageId)
						.setImageTitle("Hello World")
						.setMimeType("image/png")
						.setOriginalFileName("dict.png")
						.setWordId(1234)
						.setImageData(baos.toByteArray())
						.build();
			} catch (IOException ex) {
				throw new RuntimeException(ex);
			}
		}

		public List<Integer> getImageIdListByWordId(int wordId) {
			// TODO Auto-generated method stub
			return null;
		}

		public int getWordIdByImageId(int imageId) {
			// TODO Auto-generated method stub
			return 0;
		}
	};

	
	private final WebViewClient webViewClient = new WebViewClient(){

		@Override
		public boolean shouldOverrideUrlLoading(final WebView view, final String url) {
			String value = URLDecoder.decode(url);
			if(value.startsWith(ExplanationContentBuilder.JMP_PREFIX)){
				String text = value.substring(4);
				QuickSearchDialogBuilder.getInstance(ExplanationView.this).build(text).show();
				return true;
			}
			
			if(value.startsWith(ExplanationContentBuilder.SAY_PREFIX)){
				String text = value.substring(4);
				TextToSpeech tts = TtsManager.getTts(ExplanationView.this.getApplicationContext());
				if(tts!=null){
					tts.speak(text, 0, null);
				}
				return true;
			}
			
			if(value.startsWith(BraveImageProvider.DICT_IMG_PREFIX)) {
				int imageId = BraveImageProvider.parseUrlFormImageId(value);
				if (imageId < 0) {
					return super.shouldOverrideUrlLoading(view, url);
				}
				
				BraveImageProvider provider = new BraveImageProvider(fakeLoader);
				return provider.handleImageRequest(view, value, imageId);
			}
			
			return super.shouldOverrideUrlLoading(view, url);				
		}
	};
	
	private View.OnTouchListener onExplanationViewTouchListener = new View.OnTouchListener() {
		
		private KeyEvent shiftPressEvent= new KeyEvent(0, 0,
				KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_SHIFT_LEFT, 0, 0);

		public boolean onTouch(View v, MotionEvent event) {
				shiftPressEvent.dispatch(v);
				caught.set(false);
				v.setOnTouchListener(null);
				return false;
		}
	};

	protected void fillSpeakAllData(){
		if(explain == null || explain.length() == 0)
			return;
		
		speakingAll = !speakingAll;
		if(!speakingAll){
			WebView tvExplain = (WebView) this.findViewById(R.id.explan_explan);
			fillExplain(explain, tvExplain);	
			return;
		}			
		
		Matcher m = null;
		String exp = explain;
		
		m = Pattern.compile("&\\w+;").matcher(exp);
		exp = m.replaceAll(" ");
		
		m = Pattern.compile("\\[([^\\[\\]]+)\\]").matcher(exp);
		exp = m.replaceAll(" $1 ");
		
		m = Pattern.compile("<[^>]+>").matcher(exp);
		exp = m.replaceAll(" ");
		
		m = SPEAK_ALL_PATTERN.matcher(exp);
		
		exp = m.replaceAll("$1[$2]$3");
		
		
		WebView tvExplain = (WebView) this.findViewById(R.id.explan_explan);
		fillExplain(exp, tvExplain);				
	}
	
	public void fillCaseData() {
		this.speakingAll = false;
		
		BraveDictDbAdapter dbHelper = BraveDictDbAdapter
				.getInstance(getApplicationContext());

		List<String> nexts = null;
		nexts = dbHelper.getCaseNextWords(word, 20);

		StringBuilder sb = new StringBuilder();
		Iterator<String> it = nexts.iterator();
		while (it.hasNext()) {
			sb.append("[").append(it.next()).append("]");
			if (it.hasNext())
				sb.append("\n");
		}

		WebView tvExplan = (WebView) this.findViewById(R.id.explan_explan);
		fillExplain(sb.toString(), tvExplan);
	}
	
	public void fillRangeData(boolean up){
		this.speakingAll = false;

		BraveDictDbAdapter dbHelper = BraveDictDbAdapter.getInstance(getApplicationContext());
		
		List<String> nexts = null;
		if(!up)
			nexts = dbHelper.getNextWords(word, 20);
		else
			nexts = dbHelper.getPrevWords(word, 20);
		
		StringBuilder sb = new StringBuilder();
		Iterator<String> it = nexts.iterator();
		while(it.hasNext()){
			sb.append("[")
			.append(it.next())
			.append("]");
			if(it.hasNext())
				sb.append("\n");				
		}
		
		WebView tvExplan = (WebView)this.findViewById(R.id.explan_explan);
		fillExplain(sb.toString(), tvExplan);
	}
	
	public void fillData()
	{		
		this.speakingAll = false;

		BraveDictDbAdapter dbHelper = BraveDictDbAdapter.getInstance(getApplicationContext());
		TextView tv = (TextView)findViewById(R.id.TVDatabaseName);
		tv.setText(dbHelper.getDatabaseShortName());		
		Cursor c = dbHelper.getExplanation(word);
		//this.startManagingCursor(c);
		
		TextView tvWord = (TextView)this.findViewById(R.id.explan_word);
		tvWord.setText(word);
		WebView tvExplan = (WebView)this.findViewById(R.id.explan_explan);
		if(c.moveToFirst()==false){
			c.close();
			fillRangeData(false);
		}else{
			QuickHist.getInstance().add(word);
			
			String explan = c.getString(1);
			c.close();
			//if(explan != null && explan.length()>0){
			//	explan = explan.replaceAll("\\\\n", "\n");
			//}
			
			this.explain = explan;
			fillExplain(explan, tvExplan);
		}	
		
	}
	
	private void fillExplain(String explain, final WebView tv){
		
		String allHistories = QuickHist.getInstance().getAllHistories("[", ", ", "]");		
		CharSequence sb = buildExplanationContent(explain, allHistories);
		
		tv.setWebViewClient(webViewClient);
		tv.loadData(sb.toString(), "text/html", "utf-8");
		//tv.loadDataWithBaseURL("fake://not/needed", sb.toString(), "text/html", "utf-8", "");
		
		tv.getSettings().setBuiltInZoomControls(true);
		tv.getSettings().setSupportZoom(true);		
		
	}

	public CharSequence buildExplanationContent(String explain, String allHistories) {
		BraveDictDbAdapter db = BraveDictDbAdapter.getInstance(this);
		int wordId = db.getWordId(this.word);
		List<Integer> imageIdList = Collections.<Integer>emptyList();
		ImageDataLoader dataLoader = db.createImageDataLoader();
		if(wordId >= 0)
			imageIdList = dataLoader.getImageIdListByWordId(wordId);
		
		return new ExplanationContentBuilder()
			.setExplanation(explain)
			.setHistoryText(allHistories)
			.setImageProvider(new BraveImageProvider(dataLoader))
			.setImageIdList(imageIdList)
			.build();
	}

	@Override
	public void onConfigurationChanged(Configuration newConfig) {
		// TODO Auto-generated method stub
		super.onConfigurationChanged(newConfig);
	}

    private final static class QuickSearchDialogBuilder {
    	private final ExplanationView parent;
    	private String text;
    	private EditText edit;
    	private FrameLayout layout;
    	private AlertDialog alert;
    	
    	private DialogInterface.OnClickListener okButtonListener =
    		new DialogInterface.OnClickListener() {

				public void onClick(DialogInterface dialog, int which) {
					text = edit.getText().toString().trim();
					if(text.length()==0)
						return;
					
					parent.setWord(text);
					parent.fillData();
					
				}
    			
    	};
    	
    	private DialogInterface.OnClickListener cancelButtonListener =
    		new DialogInterface.OnClickListener() {

				public void onClick(DialogInterface dialog, int which) {
					return;					
				}
    		
    	};
    	
    	private static QuickSearchDialogBuilder instance;
    	
    	private QuickSearchDialogBuilder(ExplanationView parent){
    		this.parent = parent;
    		edit = new EditText(parent);
    		LayoutInflater inflater = parent.getLayoutInflater();
    		layout = (FrameLayout)inflater.inflate(R.layout.dialog_layout, null);
    		layout.addView(edit, 
    				new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 
    						ViewGroup.LayoutParams.WRAP_CONTENT));
    	}
    	
    	public static synchronized QuickSearchDialogBuilder getInstance(ExplanationView ev){
    		if(instance == null || instance.parent != ev){
    			instance = new QuickSearchDialogBuilder(ev);
    		}
    		return instance;
    	}

    	public AlertDialog build(String text) {
    		this.text = text;
    		edit.setText(text);
    		if(alert == null)
    			alert = createDialog();
    		alert.setView(layout);
    		return alert;
    	}
    	
    	protected AlertDialog createDialog(){
    		return new AlertDialog.Builder(parent)
    			.setTitle("Edit Search")
    			.setPositiveButton("OK", okButtonListener)
    			.setNegativeButton("Cancel", cancelButtonListener)
    			.create();
    	}
    }
    
    @Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		super.onActivityResult(requestCode, resultCode, data);
		
		Bundle extras = null;
		
		switch(requestCode){
		case DictConst.ACTIVITY_CHOOSE_DB:
			//mDictMessageHandler = null;
			if(data == null){
				return;
			}
			extras = data.getExtras();
			String db_name = extras.getString("DB_NAME");
			if(db_name==null){
				return;
			}
			try{
				BraveDictDbAdapter dbHelper = BraveDictDbAdapter.getInstance(this);
				String oldName = dbHelper.getDatabaseName();
				if(oldName!=null && oldName.equals(db_name)){
					return;
				}
				dbHelper.close();
				dbHelper.setDbName(db_name);
				
				BraveConfigManager cm = BraveConfigManager.getManager(getApplicationContext());
				cm.setDictName(dbHelper.getDatabaseShortName());
				
				dbHelper.open();
				fillData();
			}catch(FileNotFoundException fnfe){
				//should turn to runtime exception
				throw new RuntimeException(fnfe);
			}
			break;
		}
    }
}
